package com.hgx.lc.core.infrastructure.persistence.factory;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.resource.NoResourceException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.DbRuntimeException;
import cn.hutool.db.DbUtil;
import cn.hutool.db.Entity;
import cn.hutool.db.dialect.DriverUtil;
import cn.hutool.db.ds.DSFactory;
import cn.hutool.db.ds.DataSourceWrapper;
import cn.hutool.setting.Setting;
import cn.hutool.setting.dialect.Props;
import com.alibaba.druid.pool.DruidDataSource;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Description: 自定义druid DSFactory
 * author: HGX
 * DateTime: 2020-11-30 15:16
 */
public class DruidCustomDSFactory extends DSFactory {

    /**
     * 数据库配置文件可选路径2
     */
    private static final String DEFAULT_DB_SETTING_PATH2 = "db.setting";

    /**
     * 数据库连接配置文件
     */
    private Setting setting;

    private Map<String, DataSourceWrapper> dsMap;

    public DruidCustomDSFactory() {
        this("DruidCustom");
    }

    public DruidCustomDSFactory(String dataSourceName) {
        super(dataSourceName);
        //此参数的作用是在detectDSFactory方法自动检测所用连接池时，如果实现类不存在，调用此方法会自动抛出异常，从而切换到下一种连接池的检测。
        Assert.notNull(DruidDataSource.class);
        if (null == setting) {
            // 尝试ClassPath下直接读取配置文件
            try {
                setting = new Setting(DEFAULT_DB_SETTING_PATH2, true);
            } catch (NoResourceException e2) {
                throw new NoResourceException("Default db setting [{}] or [{}] in classpath not found !", DEFAULT_DB_SETTING_PATH2, DEFAULT_DB_SETTING_PATH2);
            }

        }

        // 读取配置，用于SQL打印
        DbUtil.setShowSqlGlobal(setting);

        this.dsMap = new ConcurrentHashMap<>();
    }

    @Override
    public DataSource getDataSource(String s) {

        //加载默认数据源
        DataSourceWrapper dataSourceWrapper = this.dsMap.get("");
        if (dataSourceWrapper == null) {
            DataSourceWrapper ds = this.createDataSource("");
            this.dsMap.put("", ds);
        }


        DataSourceWrapper existedDataSource = this.dsMap.get(s == null ? "" : s);
        if (existedDataSource != null) {
            return existedDataSource;
        } else {
            DataSourceWrapper ds = this.createDataSource(s);
            this.dsMap.put(s, ds);
            return ds;
        }
    }

    @Override
    public void close(String s) {
        if (s == null) {
            s = StrUtil.EMPTY;
        }

        DataSourceWrapper ds = dsMap.get(s);
        if (ds != null) {
            ds.close();
            dsMap.remove(s);
        }
    }

    @Override
    public void destroy() {
        if (CollectionUtil.isNotEmpty(dsMap)) {
            Collection<DataSourceWrapper> values = dsMap.values();
            for (DataSourceWrapper ds : values) {
                ds.close();
            }
            dsMap.clear();
        }
    }


    private DataSourceWrapper createDataSource(String s) {
        final Setting config = setting.getSetting("");
        if (s == null) {
            s = "";
        }

        try {

            if (!"".equals(s)) {
                Entity e = Db.use().get("data_source", "db_code", s);
                String url = e.getStr("url");
//                String driver = e.getStr("com.mysql.jdbc.Driver");
                String user = e.getStr("user_name");
                String pass = e.getStr("password");
                return DataSourceWrapper.wrap(this.createDataSource(url, null, user, pass, config), null);
            } else {
                if (CollectionUtil.isEmpty(config)) {
                    throw new DbRuntimeException("No config for group: [{}]", s);
                }

                // 基本信息
                final String url = config.getAndRemoveStr(KEY_ALIAS_URL);
                if (StrUtil.isBlank(url)) {
                    throw new DbRuntimeException("No JDBC URL for group: [{}]", s);
                }
                // 自动识别Driver
                String driver = config.getAndRemoveStr(KEY_ALIAS_DRIVER);
                if (StrUtil.isBlank(driver)) {
                    driver = DriverUtil.identifyDriver(url);
                }
                final String user = config.getAndRemoveStr(KEY_ALIAS_USER);
                final String pass = config.getAndRemoveStr(KEY_ALIAS_PASSWORD);

                return DataSourceWrapper.wrap(createDataSource(url, driver, user, pass, config), driver);
            }
        } catch (SQLException e) {
            return null;
        }

    }

    protected DataSource createDataSource(String jdbcUrl, String driver, String user, String pass, Setting poolSetting) {
        final DruidDataSource ds = new DruidDataSource();

        // 基本信息
        ds.setUrl(jdbcUrl);
        ds.setDriverClassName(driver);
        ds.setUsername(user);
        ds.setPassword(pass);

        // remarks等特殊配置，since 5.3.8
        String connValue;
        for (String key : KEY_CONN_PROPS) {
            connValue = poolSetting.getAndRemoveStr(key);
            if (StrUtil.isNotBlank(connValue)) {
                ds.addConnectionProperty(key, connValue);
            }
        }

        // Druid连接池配置信息，规范化属性名
        final Props druidProps = new Props();
        poolSetting.forEach((key, value) -> druidProps.put(StrUtil.addPrefixIfNot(key, "druid."), value));

        druidProps.setProperty("druid.pass", user);
        druidProps.setProperty("druid.url", jdbcUrl);
        druidProps.setProperty("druid.user", pass);
        ds.configFromPropety(druidProps);

        // 检查关联配置，在用户未设置某项配置时，
        if (null == ds.getValidationQuery()) {
            // 在validationQuery未设置的情况下，以下三项设置都将无效
            ds.setTestOnBorrow(false);
            ds.setTestOnReturn(false);
            ds.setTestWhileIdle(false);
        }

        return ds;
    }
}
